#include<cstring>
#include<sys/epoll.h>
#include<iostream>
#include"Sock.hpp"
#include<sys/epoll.h>
#include<unistd.h>
int main()
{
  int listen_sock = Sock::Socket();
   Sock::Bind(listen_sock,8080);
    Sock::Listen(listen_sock);

  //创建epoll模型，获得文件描述符epfd
  int epfd = epoll_create(1000);
  
  //添加epoll关心的事件
  struct epoll_event ev;
  ev.events = EPOLLIN;
  ev.data.fd  = listen_sock;

  epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev);
  //事件循环检测,事件是否就绪
  volatile bool flag = false;
  struct epoll_event revs[64];
  while(!flag)
  {
    int timeout = -1;//0:非阻塞，-1：阻塞,其他：超时时间
    //这里传入的revs仅仅是尝试从内核中能拿到已经就绪事件fd给用户
    int n = epoll_wait(epfd,revs,64,timeout);
    switch(n)
    {
case-1:
  std::cout<<"出错...epool_wait...."<<std::endl;
  break;
case 0:
  std::cout<<"timeout...."<<std::endl;
  break;

default:
  //关心的fd有事件就绪，但是不知道是什么事件就绪（读还是写？还是异常）
  std::cout<<"关心的fd集合有事件就绪，但是不知道，就绪事件是什么，不知道是哪个fd事件就绪..."<<std::endl;
 for(int i = 0;i<n;i++)
 {
    int sock = revs[i].data.fd;
    std::cout<<"有事件就绪了,就绪事件文件描述符："<<sock<<"..."<<std::endl;
    if(revs[i].events & EPOLLIN )
    {
      std::cout<<"就绪事件是读事件，但是不知道是哪个fd对应的读事件..."<<std::endl;
      if(sock == listen_sock)
      {
        std::cout<<"文件描述符:"<<sock<<"的读事件就绪,这是listen_sock的读就绪事件..."<<std::endl;
        //这里调用accept就不会阻塞
        int fd = Sock::Accept(listen_sock);
        if(fd >=0)
        {
            //获取到新的通信描述符，加入到epoll模型进行监听事件
            struct epoll_event _ev;
            _ev.events = EPOLLIN;
            _ev.data.fd = fd;
            epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&_ev);
            std::cout<<"添加新连接的文件描述符："<<fd<<"到了epoll模型中..."<<std::endl;
        }
        else{
              //获取连接失败
        }
      }
      else 
      {
        std::cout<<"有数据到来的读事件就绪，也就是通信套接字文件描述符:"<<sock<<"读事件就绪..."<<std::endl;
        char buf[1024];
        memset(buf,'\0',sizeof(buf));
        /**************************************************
         *：注意：1.这里最大的问题是你能保证你读取一次就可以读取完fd里面的缓冲区数据吗？
         *          一次没读完，那么你只能等下次epoll_wait检测到该文件描述符fd数据就绪时候再读了
         *          但是，你能够保证下次就绪事件的文件描述符就是上次没有读完的fd吗？肯定不能
         *          那么新的就绪事件fd的数据被读到就会覆盖你的用户层还没有接收完数据的buf了;
         *          又或者你的buf是局部的，下一次再读，该buf也是新的buf了，就buf数据也别释放了;
         *
         *
         *       2. 或者你多读了呢？那就需要你协议进行处理,那假如你没有处理呢？
         *
         *************************************************/
        ssize_t sz = recv(sock,buf,sizeof(buf)-1,0);
        if(sz >0)
        {
          buf[sz] = '\0';
          std::cout<<"client["<<sock<<"]# "<<buf<<std::endl;
          //处理完读事件后
          //假如我们要处理写事件,那么就将关心的事件更改为EPOLLOUT就行
          
          //////////////////////////////////
          //这中写法还是有漏洞的，不建议这么写
          //////////////////////////////////
//          struct epoll_event _ev;
//          _ev.events =EPOLLOUT;
//          _ev.data.fd = sock;
//          epoll_ctl(epfd,EPOLL_CTL_MOD,sock,&_ev);
//
        }
        else if(sz == 0)
        {
            std::cout<<"对端关闭连接...."<<std::endl;
            close(sock);
            epoll_ctl(epfd,EPOLL_CTL_DEL,sock,nullptr);
            std::cout<<sock<<"delete for, epoll..."<<std::endl;
        }
        else{
            std::cout<<"读取出错...."<<std::endl;
            close(sock);
            epoll_ctl(epfd,EPOLL_CTL_DEL,sock,nullptr);
            std::cout<<sock<<" delete for, epoll..."<<std::endl;

        }
      }
    }
    else if(revs[i].events & EPOLLOUT)
    {
      //写事件
    }
    else{
      //异常事件
    }
 }
  break;
    }
  }

  close(listen_sock);
  close(epfd);
  return 0;
}
